#include <stdlib.h>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////
int NoeudSeqInst::getValeur() {
  int valeur=0;
  for (unsigned int i=0; i<tabInst.size(); i++)
    valeur = tabInst[i]->getValeur();  // on evalue chaque instruction de la séquence
  return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
  for (unsigned int i=0; i<tabInst.size(); i++)
    tabInst[i]->afficher(indentation+1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::afficherPHP(unsigned short indentation) {
  for (unsigned int i=0; i<tabInst.size(); i++)
    tabInst[i]->afficherPHP(); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
  tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstSi
////////////////////////////////////////////////////////////////////////////////

NoeudInstSi::NoeudInstSi() {
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstSi::getValeur() {
    int i=0;
    int valeur=0;
    while(i<vcond.size() && vcond[i]!=NULL && !vcond[i]->getValeur()){
        i++;
    }
    if(i<vcond.size()){
        valeur = vseqinst[i]->getValeur();
    }
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstSi::ajouteCondition(Noeud* cond, Noeud* seqinst) {
  vcond.push_back(cond);
  vseqinst.push_back(seqinst);
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstSi::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Si" << endl;
  vector<Noeud *>::iterator iterCond = vcond.begin();
  vector<Noeud *>::iterator iterSeq = vseqinst.begin();
  (**iterCond).afficher(indentation+1);
  (**iterSeq).afficher(indentation+1);
  iterSeq++;
  iterCond++;
  while(iterSeq<vseqinst.end() && iterCond<vcond.end() && *iterCond != NULL)
  {
      Noeud::afficher(indentation);
      cout << "Noeud - Sinon si" << endl;
      (**iterCond).afficher(indentation+1);
      (**iterSeq).afficher(indentation+1);
      iterSeq++;
      iterCond++;
  }
  if(iterCond!=vcond.end())
  {
      Noeud::afficher(indentation);
      cout << "Noeud - Sinon" << endl;
      (**iterSeq).afficher(indentation+1);
  }
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstSi::afficherPHP(unsigned short indentation) {
  vector<Noeud *>::iterator iterCond = vcond.begin();
  vector<Noeud *>::iterator iterSeq = vseqinst.begin();
  cout << "if(";
  (**iterCond).afficherPHP();
  cout << "){" << endl;
  (**iterSeq).afficherPHP(indentation+1);
  iterSeq++;
  iterCond++;
  
  while(iterSeq<vseqinst.end() && iterCond<vcond.end() && *iterCond != NULL)
  {
      cout << "}else if(";
      (**iterCond).afficherPHP(indentation+1);
      cout << "){" << endl;
      (**iterSeq).afficherPHP(indentation+1);
      iterSeq++;
      iterCond++;
  }
  if(iterCond!=vcond.end())
  {
      cout << "}else{" << endl;
      (**iterSeq).afficherPHP(indentation+1);
  }
  cout << "}" << endl;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstTq
////////////////////////////////////////////////////////////////////////////////

NoeudInstTq::NoeudInstTq(Noeud* seqinst, Noeud* cond) {
    this->seqinst=seqinst;
    this->cond=cond;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstTq::getValeur() {
    int valeur=0;
    while(cond->getValeur()){
        valeur=seqinst->getValeur();
    }

    return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstTq::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Tantque" << endl;
  cond->afficher(indentation+1);   // on affiche variable et expression
  seqinst->afficher(indentation+1);   // on affiche variable et expression
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstTq::afficherPHP(unsigned short indentation) {
  cout << "while(";
  cond->afficherPHP();   // on affiche variable et expression
  cout << "){" << endl;
  seqinst->afficherPHP(indentation+1);   // on affiche variable et expression
  cout << "}" << endl;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstRepeter
////////////////////////////////////////////////////////////////////////////////

NoeudInstRepeter::NoeudInstRepeter(Noeud* seqinst, Noeud* cond) {
    this->seqinst=seqinst;
    this->cond=cond;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstRepeter::getValeur() {
    int valeur=0;
    do{
        valeur=seqinst->getValeur();
    }while(!cond->getValeur());

    return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstRepeter::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Repeter" << endl;
  cond->afficher(indentation+1);   // on affiche variable et expression
  seqinst->afficher(indentation+1);   // on affiche variable et expression
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstRepeter::afficherPHP(unsigned short indentation) {
  cout << "do {" << endl;
  seqinst->afficherPHP(indentation+1);   // on affiche variable et expression
  cout << "}while( ";
  cond->afficherPHP(indentation+1);
  cout << ");" << endl;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstPour
////////////////////////////////////////////////////////////////////////////////

NoeudInstPour::NoeudInstPour(Noeud* seqinst, Noeud* depart, Noeud* cond, Noeud* increm) {
    this->seqinst=seqinst;
    this->cond=cond;
    this->depart=depart;
    this->increm=increm;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstPour::getValeur() {
    int valeur=0;
    for(depart->getValeur(); cond->getValeur(); increm->getValeur()) {
        valeur=seqinst->getValeur();
    }

    return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstPour::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Pour" << endl;
  cond->afficher(indentation+1);   // on affiche variable et expression
  seqinst->afficher(indentation+1);   // on affiche variable et expression
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstPour::afficherPHP(unsigned short indentation) {
  cout << "for(";
  depart->afficherPHP();
  cond->afficherPHP();   // on affiche variable et expression
  increm->afficherPHP();   // on affiche variable et expression
  cout << "){" << endl;
  seqinst->afficherPHP(indentation+1);   // on affiche variable et expression
  cout << "}" << endl;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstLire
////////////////////////////////////////////////////////////////////////////////

NoeudInstLire::NoeudInstLire(Noeud* variable) {
  this->variable=variable;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstLire::getValeur() {
  int valeur=0; // on évalue l'expression
  cin>>valeur;
  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
  return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstLire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Lire" << endl;
  variable->afficher(indentation+1);   // on affiche variable et expression
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstEcrire
////////////////////////////////////////////////////////////////////////////////

NoeudInstEcrire::NoeudInstEcrire(bool chaine, Noeud* variable) {
  this->variable=variable;
  this->chaine=chaine;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstEcrire::getValeur() {
  int valeur = 0; // on évalue l'expression
  if(this->chaine){
        cout<<((SymboleValue*)variable)->getChaine();//on ecrit la variable
  }else{
        valeur = variable->getValeur();
        cout << valeur;
  }
  return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstEcrire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Ecrire" << endl;
  variable->afficher(indentation+1);   // on affiche variable et expression
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstEcrire::afficherPHP(unsigned short indentation) {
  cout << "echo ";
  variable->afficherPHP(indentation+1);   // on affiche variable et expression
  cout << ";" << endl;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstSwitch
////////////////////////////////////////////////////////////////////////////////

NoeudInstSwitch::NoeudInstSwitch() {
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstSwitch::getValeur() {
    int i=0;
    int valeur=0;
    while(i<vcas.size() && vcas[i]!=NULL && !vcas[i]->getValeur()){
        i++;
    }
    if(i<vcas.size()){
        valeur = vseqinst[i]->getValeur();
    }
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstSwitch::ajouteCondition(Noeud* cas, Noeud* seqinst) {
  vcas.push_back(cas);
  vseqinst.push_back(seqinst);
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstSwitch::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Switch" << endl;
  vector<Noeud *>::iterator iterCas = vcas.begin();
  vector<Noeud *>::iterator iterSeq = vseqinst.begin();
  while(iterSeq<vseqinst.end() && iterCas<vcas.end() && *iterCas != NULL)
  {
      Noeud::afficher(indentation);
      cout << "Noeud - Case" << endl;
      (**iterCas).afficher(indentation+1);
      (**iterSeq).afficher(indentation+1);
      iterSeq++;
      iterCas++;
  }
  if(iterCas!=vcas.end())
  {
      Noeud::afficher(indentation);
      cout << "Noeud - Default" << endl;
      (**iterSeq).afficher(indentation+1);
  }
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
  this->variable=variable;
  this->expression=expression;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudAffectation::getValeur() {
  int valeur = expression->getValeur(); // on évalue l'expression
  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
  return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
void NoeudAffectation::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Affectation" << endl;
  variable->afficher(indentation+1);   // on affiche variable et expression
  expression->afficher(indentation+1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudAffectation::afficherPHP(unsigned short indentation) {
  variable->afficherPHP();   // on affiche variable et expression
  cout << " = ";
  expression->afficherPHP(); // en augmentant l'indentation
  cout << ";" << endl;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire ( Symbole operateur,
                                               Noeud* operandeGauche,
                                               Noeud* operandeDroit) {
  this->operateur=operateur;
  this->operandeGauche=operandeGauche;
  this->operandeDroit=operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurBinaire::getValeur() { 
  int valeur=0;
  int og=operandeGauche->getValeur();
  int od=operandeDroit->getValeur();
  if      (this->operateur=="+") valeur = og+od; 
  else if (this->operateur=="-") valeur = og-od;
  else if (this->operateur=="*") valeur = og*od;
  else if (this->operateur=="/") {
	if (od!=0)
		valeur = og/od;
  	else {
	  	cout << "Erreur pendant l'interpretation : division par zero" << endl;
	  	exit(0); // plus tard on levera une exception
  	}
  }else if (this->operateur=="et") valeur = og&&od;
  else if (this->operateur=="ou") valeur = og||od;
  else if (this->operateur=="==") valeur = og==od;
  else if (this->operateur=="!=") valeur = og!=od;
  else if (this->operateur=="<") valeur = og<od;
  else if (this->operateur=="<=") valeur = og<=od;
  else if (this->operateur==">") valeur = og>od;
  else if (this->operateur==">=") valeur = og>=od;
  else if (this->operateur=="%") {
	if (od!=0)
		valeur = og%od;
  	else {
	  	cout << "Erreur pendant l'interpretation : modulo par zero" << endl;
	  	exit(0); // plus tard on levera une exception
  	}
  }else if (this->operateur=="non") {
      if(od==true)
          valeur=0;
      else valeur=1;
      
  }
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurBinaire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
  operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurBinaire::afficherPHP(unsigned short indentation) {
  operandeGauche->afficherPHP();  // on affiche fils gauche et fils droit
  cout << this->operateur.getChaine();
  operandeDroit->afficherPHP();   // en augmentant l'indentation

}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurUnaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurUnaire::NoeudOperateurUnaire (Symbole operateur, Noeud* operande) {
  this->operateur=operateur;
  this->operande=operande;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurUnaire::getValeur() {
  int valeur=0;
  int o=operande->getValeur();
  if(this->operateur=="non") valeur = !o;
  else // this->operateur=="-"
        valeur = - o;
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurUnaire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Unaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operande->afficher(indentation+1);  // on affiche fils gauche et fils droit
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurUnaire::afficherPHP(unsigned short indentation) {
  cout << this->operateur.getChaine();
  operande->afficherPHP();  // on affiche fils gauche et fils droit
}
